#include <cassert>
#include <iostream>
#include "curling/opengl.hpp"
#include "curling/gui.hpp"
#include "curling/stone.hpp"
#include "curling/rink.hpp"
#include "curling/game.hpp"

namespace curling {

character_t::character_t()
{
}

character_t::~character_t()
{
}

vector3d character_t::position() const
{
  return _position;
}

void character_t::set_position( vector3d p )
{
  _position = p;
}

character_t::state_t character_t::state() const
{
  return _state;
}

void character_t::animate_idle_standing()
{
  _state = state_idle;
}

void character_t::animate_idle_sitting()
{
  _state = state_idle;
}

void character_t::animate_walk( vector3d target, double speed )
{
  _state = state_walk;
  _target = target;
  _speed = speed;
}

void character_t::animate( double t, double dt )
{
  switch (_state)
  {
  case state_idle:
    break;
  
  case state_walk:
    vector3d dir = _target - _position;
    if (dir.magnitude() < 0.1)
    {
      _position = _target;
    }
    else
    {
      dir.normalize();
      _position += dir * _speed * dt;
    }
    break;
  }
}

game_character_controller_t::game_character_controller_t(
  boost::shared_ptr<game> g )
: _game(g)
{
  // create team a
  _team_a.push_back(player_t(new character_t()));
  _team_a.push_back(player_t(new character_t()));
  _team_a.push_back(player_t(new character_t()));
  _team_a.push_back(player_t(new character_t()));
  
  // create team b
  _team_b.push_back(player_t(new character_t()));
  _team_b.push_back(player_t(new character_t()));
  _team_b.push_back(player_t(new character_t()));
  _team_b.push_back(player_t(new character_t()));
}

game_character_controller_t::~game_character_controller_t()
{
}

void game_character_controller_t::animate_entrance()
{
  _state = state_entrance;
  
  vector3d upper_left(-15.0, 30.0, 0.0);
  vector3d upper_right(15.0, 30.0, 0.0);
  double speed = 1.5;
  vector3d center(0.0, 0.0, 0.0);
  
  _team_a[0]->set_position(upper_left);
  _team_a[0]->animate_walk(center, speed);
  _team_a[1]->set_position(upper_left + vector3d(-2.0, 2.0, 0.0));
  _team_a[1]->animate_walk(center, speed);
  _team_a[2]->set_position(upper_left + vector3d(-4.0, 4.0, 0.0));
  _team_a[2]->animate_walk(center, speed);
  _team_a[3]->set_position(upper_left + vector3d(-6.0, 6.0, 0.0));
  _team_a[3]->animate_walk(center, speed);
  
  _team_b[0]->set_position(upper_right);
  _team_b[0]->animate_walk(center, speed);
  _team_b[1]->set_position(upper_right + vector3d(2.0, 2.0, 0.0));
  _team_b[1]->animate_walk(center, speed);
  _team_b[2]->set_position(upper_right + vector3d(4.0, 4.0, 0.0));
  _team_b[2]->animate_walk(center, speed);
  _team_b[3]->set_position(upper_right + vector3d(6.0, 6.0, 0.0));
  _team_b[3]->animate_walk(center, speed);
}

void game_character_controller_t::animate_exit()
{
}

void game_character_controller_t::animate_begin_end()
{
}

void game_character_controller_t::animate_end_end()
{
}

void game_character_controller_t::animate_begin_shot()
{
  if (_game->player_one_turn())
  {
    // move p1 players into position
    _team_a[0]->animate_walk(skipper_start_position(), 2.0);
    _team_a[1]->animate_walk(left_sweeper_start_position(), 2.0);
    _team_a[2]->animate_walk(right_sweeper_start_position(), 2.0);
    _team_a[3]->animate_walk(aimer_start_position(), 2.0);
  
    // move p2 players into position
    _team_b[0]->animate_walk(idle_position_1(), 2.0);
    _team_b[1]->animate_walk(idle_position_2(), 2.0);
    _team_b[2]->animate_walk(idle_position_3(), 2.0);
    _team_b[3]->animate_walk(idle_position_4(), 2.0);
  }
  else
  {
    // move p2 players into position
    _team_b[0]->animate_walk(skipper_start_position(), 2.0);
    _team_b[1]->animate_walk(left_sweeper_start_position(), 2.0);
    _team_b[2]->animate_walk(right_sweeper_start_position(), 2.0);
    _team_b[3]->animate_walk(aimer_start_position(), 2.0);
  
    // move p1 players into position
    _team_a[0]->animate_walk(idle_position_1(), 2.0);
    _team_a[1]->animate_walk(idle_position_2(), 2.0);
    _team_a[2]->animate_walk(idle_position_3(), 2.0);
    _team_a[3]->animate_walk(idle_position_4(), 2.0);
  }
}

void game_character_controller_t::animate_shot()
{
  _state = state_shot;
  _team_a[0]->animate_idle_standing();
  _team_a[1]->animate_idle_standing();
  _team_a[2]->animate_idle_standing();
  _team_a[3]->animate_idle_standing();
}

void game_character_controller_t::animate_end_shot()
{
}

void game_character_controller_t::animate( double t, double dt )
{
  switch (_state)
  {
  case state_idle:
    break;
  
  case state_entrance:
    break;
  
  case state_exit:
    break;
  
  case state_begin_end:
    break;
  
  case state_end_end:
    break;
  
  case state_begin_shot:
    break;
  
  case state_shot:
  {
    // Compute the positions of the sweepers during the delivery phase. They
    // stay 0.5m ahead of the curling stone. When not sweeping they stay close
    // to the sides of the rink.
    vector3d offset = rink_offset();
    vector2d stonepos = _game->stone_played()->position();
    if (_game->player_one_turn())
    {
      if (stonepos.y() > _game->rink_played()->length() - _game->rink_played()->hog_line())
      {
        _team_a[1]->set_position(
          vector3d(left_sweeper_start_position().x(), offset.y()+stonepos.y() + 0.5, 0.0));
        _team_a[2]->set_position(
          vector3d(right_sweeper_start_position().x(), offset.y()+stonepos.y() + 0.5, 0.0));
      }
    }
    else
    {
      if (stonepos.y() > _game->rink_played()->length() - _game->rink_played()->hog_line())
      {
        _team_b[1]->set_position(
          vector3d(left_sweeper_start_position().x(), offset.y()+stonepos.y() + 0.5, 0.0));
        _team_b[2]->set_position(
          vector3d(right_sweeper_start_position().x(), offset.y()+stonepos.y() + 0.5, 0.0));
      }
    }
  } break;
  
  case state_end_shot:
    break;
  }
  
  /* Animate characters. */
  _team_a[0]->animate(t, dt);
  _team_a[1]->animate(t, dt);
  _team_a[2]->animate(t, dt);
  _team_a[3]->animate(t, dt);
  _team_b[0]->animate(t, dt);
  _team_b[1]->animate(t, dt);
  _team_b[2]->animate(t, dt);
  _team_b[3]->animate(t, dt);
}

bool game_character_controller_t::animation_completed() const
{
  return false;
}

vector3d game_character_controller_t::play_direction() const
{
  return vector3d(0.0, 1.0, 0.0);
}

vector3d game_character_controller_t::rink_offset() const
{
  return vector3d(-3.0, 0.0-(_game->rink_played()->length()/2.0), 0.0);
}

vector3d game_character_controller_t::skipper_start_position() const
{
  return rink_offset() + vector3d(0.0, _game->rink_played()->hack(), 0.0);
}

vector3d game_character_controller_t::left_sweeper_start_position() const
{
  return rink_offset() +
    vector3d(
      -(_game->rink_played()->width()/2.0)+0.5,
      _game->rink_played()->length()-_game->rink_played()->hog_line(), 0.0);
}

vector3d game_character_controller_t::right_sweeper_start_position() const
{
  return rink_offset() +
    vector3d(
      (_game->rink_played()->width()/2.0)-0.5,
      _game->rink_played()->length()-_game->rink_played()->hog_line(), 0.0);
}

vector3d game_character_controller_t::aimer_start_position() const
{
  return rink_offset() + vector3d(0.0, _game->rink_played()->back_line() + 1.0, 0.0);
}

vector3d game_character_controller_t::idle_position_1() const
{
  return rink_offset() + vector3d(-3.0, _game->rink_played()->length() + 0.75, 0.0);
}

vector3d game_character_controller_t::idle_position_2() const
{
  return rink_offset() + vector3d(-1.0, _game->rink_played()->length() + 1.0, 0.0);
}

vector3d game_character_controller_t::idle_position_3() const
{
  return rink_offset() + vector3d(1.0, _game->rink_played()->length() + 1.0, 0.0);
}

vector3d game_character_controller_t::idle_position_4() const
{
  return rink_offset() + vector3d(3.0, _game->rink_played()->length() + 0.75, 0.0);
}

gui::gui( vector2d size, double base_unit )
: _size(size),
  _base_unit(base_unit),
  _screen_fade(0.0),
  _screen_fade_speed(0.0),
  _screen_flash(0.0),
  _screen_flash_speed(0.0),
  _start_menu_visible(false),
  _main_menu_visible(false),
  _options_menu_visible(false),
  _game_menu_visible(false),
  _powermeter_visible(false),
  _player_one_winner_visible(false),
  _player_two_winner_visible(false),
  _powermeter_current_state(powermeter_state_final),
  _main_menu_selected_item(main_menu_item_play),
  _start_menu_selected_item(start_menu_item_play),
  _options_menu_selected_item(options_menu_item_none),
  _player_one_human(true),
  _player_two_human(true),
  _start_game_ends(default_game::endings),
  _start_game_stones(default_game::stones),
  _game_menu_selected_item(game_menu_item_continue),
  _camera_animation_length(0.0),
  _camera_animation_time(0.0),
  _message_time(-2.0),
  _title_music_playing(false),
  _arena_sound(false)
{
  _background_rink = boost::shared_ptr<rink>(new rink());
  
  double aspect = size.x() / size.y();
  
  _arena_camera = boost::shared_ptr<camera>(new camera(60.0, aspect));
  _rink_top_camera = boost::shared_ptr<camera>(new camera(60.0, aspect));
  _circle_top_camera = boost::shared_ptr<camera>(new camera(60.0, aspect));
  _stone_camera = boost::shared_ptr<camera>(new camera(60.0, aspect));
  
  _arena_camera->reposition(vector3d(13.0, -25.0, 13.0));
  _arena_camera->aim(vector3d(0.0, -5.0, 0.0));
  
  _rink_top_camera->reposition(vector3d(0.0, 19.9, 10.0));
  _rink_top_camera->aim(vector3d(0.0, 20.0, 0.0));
  
  _circle_top_camera->reposition(vector3d(0.0, _background_rink->circle() - 1.0, 5.0));
  _circle_top_camera->aim(vector3d(0.0, _background_rink->circle(), 0.0));
  
  _current_camera = _arena_camera;
}

gui::~gui()
{
}

vector2d gui::size() const
{
  return _size;
}

double gui::base_unit() const
{
  return _base_unit;
}

bool gui::screen_faded() const
{
  return _screen_fade >= 0.001;
}

double gui::screen_fade() const
{
  return _screen_fade;
}

void gui::fade_out( bool immediate )
{
  if (immediate)
  {
    _screen_fade = 1.0;
  }
  else
  {
    _screen_fade_speed = 0.5;
  }
}

void gui::fade_in( bool immediate )
{
  if (immediate)
  {
    _screen_fade = 0.0;
  }
  else
  {
    _screen_fade_speed = -0.5;
  }
}

bool gui::screen_flashed() const
{
  return _screen_flash >= 0.001;
}

double gui::screen_flash() const
{
  return _screen_flash;
}

void gui::flash_out()
{
  _screen_flash = 0.0;
  _screen_flash_speed = 10.0;
}

void gui::flash_in()
{
  _screen_flash = 1.0;
  _screen_flash_speed = -10.0;
}

bool gui::credits_screen_visible() const
{
  return _credits_screen_visible;
}

void gui::show_credits_screen()
{
  _credits_screen_visible = true;
}

void gui::hide_credits_screen()
{
  _credits_screen_visible = false;
}

bool gui::main_menu_visible() const
{
  return _main_menu_visible;
}

void gui::show_main_menu()
{
  _main_menu_visible = true;
  _main_menu_selected_item = main_menu_item_play;
}

void gui::hide_main_menu()
{
  _main_menu_visible = false;
}

bool gui::start_menu_visible() const
{
  return _start_menu_visible;
}

bool gui::options_menu_visible() const
{
  return _options_menu_visible;
}

void gui::show_start_menu()
{
  _start_menu_visible = true;
  _start_menu_selected_item = start_menu_item_play;
}

void gui::show_options_menu()
{
  _options_menu_visible = true;
  _options_menu_selected_item = options_menu_item_none;
}

void gui::hide_options_menu()
{
  _options_menu_visible = false;
}

void gui::hide_start_menu()
{
  _start_menu_visible = false;
}

bool gui::game_menu_visible() const
{
  return _game_menu_visible;
}

void gui::show_game_menu()
{
  _game_menu_visible = true;
  _game_menu_selected_item = game_menu_item_continue;
}

void gui::hide_game_menu()
{
  _game_menu_visible = false;
}

bool gui::scoreboard_visible() const
{
  return _scoreboard_visible;
}

void gui::show_scoreboard()
{
  _scoreboard_visible = true;
}

void gui::hide_scoreboard()
{
  _scoreboard_visible = false;
}

bool gui::player_one_winner_visible() const
{
  return _player_one_winner_visible;
}

void gui::show_player_one_winner_screen()
{
  _player_one_winner_visible = true;
}

void gui::hide_player_one_winner_screen()
{
  _player_one_winner_visible = false;
}

bool gui::player_two_winner_visible() const
{
  return _player_two_winner_visible;
}

void gui::show_player_two_winner_screen()
{
  _player_two_winner_visible = true;
}

void gui::hide_player_two_winner_screen()
{
  _player_two_winner_visible = false;
}

bool gui::powermeter_visible() const
{
  return _powermeter_visible;
}

void gui::show_powermeter()
{
  _powermeter_visible = true;
}

void gui::hide_powermeter()
{
  _powermeter_visible = false;
}

bool gui::main_quit_focus() const
{
  return _main_menu_selected_item == main_menu_item_quit;
}

bool gui::main_play_focus() const
{
  return _main_menu_selected_item == main_menu_item_play;
}

bool gui::main_options_focus() const
{
  return _main_menu_selected_item == main_menu_item_options;
}

bool gui::main_view_arena_focus() const
{
  return _main_menu_selected_item == main_menu_item_view_arena;
}

bool gui::main_view_credits_focus() const
{
  return _main_menu_selected_item == main_menu_item_view_credits;
}
/*
bool gui::start_player_one_human_selected() const
{
  return _start_menu_selected_item == start_menu_item_quit;
}
*/
bool gui::start_play_focus() const
{
  return _start_menu_selected_item == start_menu_item_play;
}

bool gui::start_options_focus() const
{
  return _start_menu_selected_item == start_menu_item_options;
}

bool gui::start_quit_focus() const
{
  return _start_menu_selected_item == start_menu_item_quit;
}



bool gui::options_player_one_human_selected() const
{
  return _player_one_human;
}

bool gui::options_player_one_ai_selected() const
{
  return not _player_one_human;
}


bool gui::options_player_one_type_focus() const
{
	return _options_menu_selected_item == options_menu_item_player1_type;
}

bool gui::options_player_two_human_selected() const
{
  return _player_two_human;
}

bool gui::options_player_two_ai_selected() const
{
  return not _player_two_human;
}



bool gui::options_player_one_team_focus() const
{
	return _options_menu_selected_item == options_menu_item_player1_team;
}

bool gui::options_player_one_human_focus() const
{
	return (_options_menu_selected_item == options_menu_item_player1_type) and
          _player_one_human;
}

bool gui::options_player_one_ai_focus() const
{
  return (_options_menu_selected_item == options_menu_item_player1_type) and
          not _player_one_human;
}

bool gui::options_player_two_team_focus() const
{
  return _options_menu_selected_item == options_menu_item_player2_team;
}

bool gui::options_player_two_type_focus() const
{
  return _options_menu_selected_item == options_menu_item_player2_type;
}

bool gui::options_player_two_human_focus() const
{
  return (_options_menu_selected_item == options_menu_item_player2_type) and
          _player_two_human;
}

bool gui::options_player_two_ai_focus() const
{
  return (_options_menu_selected_item == options_menu_item_player2_type) and
          not _player_two_human;
}

bool gui::options_game_ends_focus() const
{
	return _options_menu_selected_item == options_menu_item_game_ends;
}

bool gui::options_game_stones_focus() const
{
  return _options_menu_selected_item == options_menu_item_game_stones;
} 

bool gui::options_back_focus() const
{
  return _options_menu_selected_item == options_menu_item_back;
}

int gui::options_game_ends_selected() const
{
  return _start_game_ends;
}

int gui::options_game_stones_selected() const
{
  return _start_game_stones;
}

void gui::setup_shot(
  double power, double direction, double spin,
  double power_speed, double direction_speed, double spin_speed )
{
  assert((power >= 0.0) and (power <= 1.0));
  assert((direction >= -1.0) and (direction <= 1.0));
  assert((spin >= -1.0) and (spin <= 1.0));
  
  _powermeter_power = power;
  _powermeter_power_speed = power_speed;
  _powermeter_current_power = 0.0;
  
  _powermeter_direction = direction;
  _powermeter_direction_speed = direction_speed;
  _powermeter_current_direction = 0.0;
  
  _powermeter_spin = spin;
  _powermeter_spin_speed = spin_speed;
  _powermeter_current_spin = 0.0;
  
  _powermeter_current_state = powermeter_state_start;
  _powermeter_mode = 0.0;
}

void gui::finish_shot()
{
  _powermeter_current_state = powermeter_state_final;
}

bool gui::arrow_power_state() const
{
  return _powermeter_current_state == powermeter_state_power;
}

double gui::shadow_arrow_power() const
{
  return _powermeter_power;
}

double gui::shot_arrow_power() const
{
  return _powermeter_current_power;
}

bool gui::arrow_direction_state() const
{
  return _powermeter_current_state == powermeter_state_direction;
}

double gui::shadow_arrow_direction() const
{
  return _powermeter_direction;
}

double gui::shot_arrow_direction() const
{
  return _powermeter_current_direction;
}

bool gui::arrow_spin_state() const
{
  return _powermeter_current_state == powermeter_state_spin;
}

double gui::shadow_arrow_spin() const
{
  return _powermeter_spin;
}

double gui::shot_arrow_spin() const
{
  return _powermeter_current_spin;
}

bool gui::game_menu_continue_focus() const
{
  return _game_menu_selected_item == game_menu_item_continue;
}

bool gui::game_menu_end_focus() const
{
  return _game_menu_selected_item == game_menu_item_end;
}

boost::shared_ptr<rink> gui::background_rink()
{
  return _background_rink;
}

boost::shared_ptr<camera> gui::current_camera()
{
  return _current_camera;
}

void gui::switch_camera( boost::shared_ptr<camera> new_camera )
{
  _current_camera = new_camera;
}

void gui::animate_to_camera( boost::shared_ptr<camera> new_camera, double t )
{
  _animated_camera = _current_camera->clone();
  _animation_start_camera = _current_camera->clone();
  _animation_target_camera = new_camera;
  _camera_animation_length = t;
  _camera_animation_time = 0.0;
  _current_camera = _animated_camera;
}

boost::shared_ptr<camera> gui::arena_camera()
{
  return _arena_camera;
}

boost::shared_ptr<camera> gui::rink_top_camera()
{
  return _rink_top_camera;
}

boost::shared_ptr<camera> gui::circle_top_camera()
{
  return _circle_top_camera;
}

boost::shared_ptr<camera> gui::stone_camera()
{
  return _stone_camera;
}

void gui::attach_game( boost::shared_ptr<game> new_game )
{
  _current_game = new_game;
  
  // create character controller
  _game_character_controller =
    boost::shared_ptr<game_character_controller_t>(
      new game_character_controller_t(_current_game));
      
  // inform listeners
  for (size_t i=0; i<_listeners.size(); ++i)
  {
    _listeners[i]->game_attached(new_game);
  }
}

void  gui::detach_game()
{
  for (size_t i=0; i<_listeners.size(); ++i)
  {
    _listeners[i]->game_detached(_current_game);
  }
  _current_game.reset();
}

boost::shared_ptr<game> gui::current_game()
{
  return _current_game;
}

boost::shared_ptr<game_character_controller_t> gui::game_character_controller() const
{
  return _game_character_controller;
}

bool gui::title_music_playing() const
{
  return _title_music_playing;
}

void gui::play_title_music()
{
  if (not _title_music_playing)
  {
    _title_music_playing = true;
    for (size_t i=0; i<_listeners.size(); ++i)
    {
      _listeners[i]->play_title_music();
    }
  }
}

void gui::stop_title_music()
{
  if (_title_music_playing)
  {
    _title_music_playing = false;
    for (size_t i=0; i<_listeners.size(); ++i)
    {
      _listeners[i]->stop_title_music();
    }
  }
}

bool gui::arena_sound() const
{
  return _arena_sound;
}

void gui::start_arena_sound()
{
  _arena_sound = true;
  for (size_t i=0; i<_listeners.size(); ++i)
  {
    _listeners[i]->start_arena_sound();
  }
}

void gui::stop_arena_sound()
{
  _arena_sound = false;
  for (size_t i=0; i<_listeners.size(); ++i)
  {
    _listeners[i]->stop_arena_sound();
  }
}

void gui::display_message( const std::string& message, double t )
{
  _message = message;
  _message_time = t;
}

void gui::remove_message()
{
  _message = "";
  _message_time = -2.0;
}

const std::string& gui::message_displayed() const
{
  return _message;
}

double gui::message_time() const
{
  return _message_time;
}

void gui::key_down( double t, int key, char character )
{
  if (main_menu_visible() and (key == GLFW_KEY_UP))
  {
    switch (_main_menu_selected_item)
    {
    case main_menu_item_play:
      break;
    
    case main_menu_item_options:
      _main_menu_selected_item = main_menu_item_play;
      break;
    
    case main_menu_item_view_arena:
      _main_menu_selected_item = main_menu_item_options;
      break;
    
    case main_menu_item_view_credits:
      _main_menu_selected_item = main_menu_item_view_arena;
      break;
    
    case main_menu_item_quit:
      _main_menu_selected_item = main_menu_item_view_credits;
      break;
    }
  }
  
  if (main_menu_visible() and (key == GLFW_KEY_DOWN))
  {
    switch (_main_menu_selected_item)
    {
    case main_menu_item_play:
      _main_menu_selected_item = main_menu_item_options;
      break;
    
    case main_menu_item_options:
      _main_menu_selected_item = main_menu_item_view_arena;
      break;
    
    case main_menu_item_view_arena:
      _main_menu_selected_item = main_menu_item_view_credits;
      break;
    
    case main_menu_item_view_credits:
      _main_menu_selected_item = main_menu_item_quit;
      break;
    
    case main_menu_item_quit:
      break;
    }
  }
      
  if (start_menu_visible() and (key == GLFW_KEY_UP))
  {
    switch (_start_menu_selected_item)
    {
    case start_menu_item_quit:
      _start_menu_selected_item = start_menu_item_options;
      focus_item();
      break;
    case start_menu_item_play:
      break;

	case start_menu_item_options:
		_start_menu_selected_item = start_menu_item_play;
		focus_item();
		break;
      
   /* case start_menu_item_player_one_team:
      // select previous team
      break;
    
    case start_menu_item_player_one_type:
      if (not _player_one_human)
      {
        _player_one_human = true;
        focus_item();
      }
      break;
    
    case start_menu_item_player_two_team:
      // select previous team
      break;
    
    case start_menu_item_player_two_type:
      if (not _player_two_human)
      {
        _player_two_human = true;
        focus_item();
      }
      break;
      
    case start_menu_item_game_ends:
      _start_menu_selected_item = start_menu_item_play;
      focus_item();
      break;
    
    case start_menu_item_game_stones:
      _start_menu_selected_item = start_menu_item_game_ends;
      focus_item();
      break;*/
      
    default:
      assert(false);
      break;
    }
  }
  
  if (start_menu_visible() and (key == GLFW_KEY_DOWN))
  {
    switch (_start_menu_selected_item)
    {
    case start_menu_item_quit:
      // do nothing
      break;
    
    case start_menu_item_play:
      _start_menu_selected_item = start_menu_item_options;
      focus_item();
      break;

	case start_menu_item_options:
		_start_menu_selected_item = start_menu_item_quit;
		break;
    /*case start_menu_item_player_one_team:
      // select next team
      break;
    
    case start_menu_item_player_one_type:
      if (_player_one_human)
      {
        _player_one_human = false;
        focus_item();
      }
      break;
    
    case start_menu_item_player_two_team:
      // select next team
      break;
    
    case start_menu_item_player_two_type:
      if (_player_two_human)
      {
        _player_two_human = false;
        focus_item();
      }
      break;
    
    case start_menu_item_game_ends:
      _start_menu_selected_item = start_menu_item_game_stones;
      focus_item();
      break;
    
    case start_menu_item_game_stones:
      _start_menu_selected_item = start_menu_item_quit;
      focus_item();
      break;*/
      
    default:
      assert(false);
      break;
    }
  }
  
 /* if (start_menu_visible() and (key == GLFW_KEY_LEFT))
  {
    switch (_start_menu_selected_item)
    {
    case start_menu_item_game_ends:
    case start_menu_item_game_stones:
    case start_menu_item_quit:
    case start_menu_item_play:
      _start_menu_selected_item = start_menu_item_player_one_type;
      focus_item();
      break;
    
    case start_menu_item_player_one_team:
      // do nothing
      break;
    
    case start_menu_item_player_one_type:
      _start_menu_selected_item = start_menu_item_player_one_team;
      focus_item();
      break;
    
    case start_menu_item_player_two_team:
      _start_menu_selected_item = start_menu_item_player_two_type;
      focus_item();
      break;
    
    case start_menu_item_player_two_type:
      _start_menu_selected_item = start_menu_item_play;
      focus_item();
      break;

    default:
      assert(false);
      break;
    }
  }
  
  if (start_menu_visible() and (key == GLFW_KEY_RIGHT))
  {
    switch (_start_menu_selected_item)
    {
    case start_menu_item_game_ends:
    case start_menu_item_game_stones:
    case start_menu_item_quit:
    case start_menu_item_play:
      _start_menu_selected_item = start_menu_item_player_two_type;
      focus_item();
      break;
    
    case start_menu_item_player_one_team:
      _start_menu_selected_item = start_menu_item_player_one_type;
      focus_item();
      break;
    
    case start_menu_item_player_one_type:
      _start_menu_selected_item = start_menu_item_play;
      focus_item();
      break;
    
    case start_menu_item_player_two_team:
      // do nothing
      break;
    
    case start_menu_item_player_two_type:
      _start_menu_selected_item = start_menu_item_player_two_team;
      focus_item();
      break;
      
    default:
      assert(false);
      break;
    }
  }*/
  
  if (main_menu_visible() && ((key == GLFW_KEY_SPACE) || (key == GLFW_KEY_ENTER)))
  {
    switch (_main_menu_selected_item)
    {
    case main_menu_item_play:
      select_item();
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->play();
      }
      break;
    
    case main_menu_item_quit:
      select_item();
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->exit();
      }
      break;
    
    default:
      // need to add for the other items as well
      break;
    }
  }
  
  else
  
  if (start_menu_visible() && ((key == GLFW_KEY_SPACE) || (key == GLFW_KEY_ENTER)))
  {
    switch (_start_menu_selected_item)
    {
    case start_menu_item_play:
      select_item();
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->play();
      }
      break;
    
    case start_menu_item_quit:
      select_item();
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->exit();
      }
      break;

	case start_menu_item_options:
		select_item();
		for (int i=0; i<_listeners.size(); ++i)
		{
			_listeners[i]->options();
		}
    break;
 /*   case start_menu_item_player_one_team:
    case start_menu_item_player_one_type:
    case start_menu_item_player_two_team:
    case start_menu_item_player_two_type:
      // do nothing
      break;
    
    case start_menu_item_game_ends:
      select_item();
      switch (_start_game_ends)
      {
      case 4: _start_game_ends = 6; break;
      case 6: _start_game_ends = 8; break;
      case 8: _start_game_ends = 4; break;
      default: assert(false); break;
      }
      break;
    
    case start_menu_item_game_stones:
      select_item();
      switch (_start_game_stones)
      {
      case 4: _start_game_stones = 6; break;
      case 6: _start_game_stones = 8; break;
      case 8: _start_game_stones = 4; break;
      default: assert(false); break;
      }
      break;
      */
    default:
      assert(false);
      break;
    }
  }

  if (options_menu_visible() and (key == GLFW_KEY_UP)) {

	  switch (_options_menu_selected_item) {

		case options_menu_item_player1_type:
			  /*if (not _player_one_human)
			  {
				_player_one_human = true;
				focus_item();
			  }*/

			  break;

		case options_menu_item_player2_type:
			_options_menu_selected_item = options_menu_item_player1_team;
		 /* if (not _player_two_human)
		  {
			_player_two_human = true;
			focus_item();
		  }*/
			
		  break;
    
		case options_menu_item_player1_team:
			_options_menu_selected_item = options_menu_item_player1_type;
			break;

		case options_menu_item_player2_team:
			_options_menu_selected_item = options_menu_item_player2_type;
			break;

		case options_menu_item_game_ends:
			_options_menu_selected_item = options_menu_item_player2_team;
			break;

		case options_menu_item_game_stones:
			_options_menu_selected_item = options_menu_item_game_ends;
			break;

		case options_menu_item_back:
			_options_menu_selected_item = options_menu_item_game_stones;
			break;

		case options_menu_item_none:
			_options_menu_selected_item = options_menu_item_back;
			break;
		
	  }
  }

  if (options_menu_visible() and (key == GLFW_KEY_DOWN))
  {
	  switch (_options_menu_selected_item) {

		case options_menu_item_player1_type:
			_options_menu_selected_item = options_menu_item_player1_team;
			  /*if (not _player_one_human)
			  {
				_player_one_human = true;
				focus_item();
			  }*/

			  break;

		case options_menu_item_player2_type:
			_options_menu_selected_item = options_menu_item_player2_team;
		 /* if (not _player_two_human)
		  {
			_player_two_human = true;
			focus_item();
		  }*/
			
		  break;
    
		case options_menu_item_player1_team:
			_options_menu_selected_item = options_menu_item_player2_type;
			break;

		case options_menu_item_player2_team:
			_options_menu_selected_item = options_menu_item_game_ends;
			break;

		case options_menu_item_game_ends:
			_options_menu_selected_item = options_menu_item_game_stones;
			break;

		case options_menu_item_game_stones:
			_options_menu_selected_item = options_menu_item_back;
			break;
		
		case options_menu_item_none:
			_options_menu_selected_item = options_menu_item_player1_type;
			break;

		case options_menu_item_back:
			
			break;

	  }
  }

  if (options_menu_visible() && ((key == GLFW_KEY_SPACE) || (key == GLFW_KEY_ENTER)))
  {
	  switch(_options_menu_selected_item ) {
		  case options_menu_item_back:
			select_item();
			for (int i=0; i<_listeners.size(); ++i)
			{
				_listeners[i]->exit();
			}
			break;

		case options_menu_item_game_ends:
			select_item();
			switch (_start_game_ends)
			{
				case 4: _start_game_ends = 6; break;
				case 6: _start_game_ends = 8; break;
				case 8: _start_game_ends = 4; break;
				default: assert(false); break;
			}
			break;
    
		case options_menu_item_game_stones:
		  select_item();
		  switch (_start_game_stones)
		  {
			case 4: _start_game_stones = 6; break;
			case 6: _start_game_stones = 8; break;
			case 8: _start_game_stones = 4; break;
			default: assert(false); break;
		  }
		  break;

		case options_menu_item_player1_type:
		  select_item();
		  _player_one_human = !options_player_one_human_selected();

		  break;

		case options_menu_item_player2_type:
		  select_item();
		  _player_two_human = !options_player_two_human_selected();

		  break;

	  }



  }
  
  if (game_menu_visible() and (key == GLFW_KEY_UP))
  {
    switch (_game_menu_selected_item)
    {
    case game_menu_item_continue:
      // do nothing
      break;
    
    case game_menu_item_end:
      _game_menu_selected_item = game_menu_item_continue;
      focus_item();
      break;
      
    default:
      assert(false);
      break;
    }
  }
  
  if (game_menu_visible() and (key == GLFW_KEY_DOWN))
  {
    switch (_game_menu_selected_item)
    {
    case game_menu_item_continue:
      _game_menu_selected_item = game_menu_item_end;
      focus_item();
      break;
    
    case game_menu_item_end:
      // do nothing
      break;
    
    default:
      assert(false);
      break;
    }
  }
  
  if (game_menu_visible() and ((key == GLFW_KEY_SPACE) or (key == GLFW_KEY_ENTER)))
  {
    switch (_game_menu_selected_item)
    {
    case game_menu_item_continue:
      select_item();
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->resume();
      }
      break;
    
    case game_menu_item_end:
      select_item();
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->exit();
      }
      break;
    
    default:
      assert(false);
      break;
    }
  }
  
  if ((not start_menu_visible()) and
      (not game_menu_visible()) and
      current_game() and
      (key == GLFW_KEY_ESC))
  {
    for (int i=0; i<_listeners.size(); ++i)
    {
      _listeners[i]->pause();
    }
  }
  else if (game_menu_visible() and
      current_game() and
      (key == GLFW_KEY_ESC))
  {
    for (int i=0; i<_listeners.size(); ++i)
    {
      _listeners[i]->resume();
    }
  }
  
  /* Camera angles. */
  if ((not start_menu_visible()) and
      (not game_menu_visible()) and
      current_game())
  {
    switch (key)
    {
    case GLFW_KEY_UP:
      // switch to home camera
      break;
    
    case GLFW_KEY_DOWN:
      // switch to ice camera
      break;
    
    default:
      break;
    }
  }
  
  if (powermeter_visible() && (key == GLFW_KEY_SPACE))
  {
    switch (_powermeter_current_state)
    {
    case powermeter_state_start:
      _powermeter_current_state = powermeter_state_power;
      _powermeter_mode = 1.0;
      break;
  
    case powermeter_state_power:
      _powermeter_current_state = powermeter_state_direction;
      _powermeter_mode = 1.0;
      break;
    
    case powermeter_state_direction:
      _powermeter_current_state = powermeter_state_spin;
      _powermeter_mode = 1.0;
      break;
    
    case powermeter_state_spin:
      _powermeter_current_state = powermeter_state_final;
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->player_deliver(
          _powermeter_current_direction,
          _powermeter_current_power,
          _powermeter_current_spin);
      }
      break;
    
    case powermeter_state_final:
      // do nothing
      break;
    
    default:
      assert(false);
      break;
    }
  }
}

void gui::key_up( double t, int key, char character )
{
  /* Camera angles. */
  if ((not start_menu_visible()) and
      (not game_menu_visible()) and
      current_game())
  {
    switch (key)
    {
    case GLFW_KEY_DOWN:
    case GLFW_KEY_UP:
      // switch to main person camera
      break;
    
    default:
      break;
    }
  }
}

void gui::animate( double t, double dt )
{
  /* Update screen fade */
  if ((_screen_fade_speed <= -0.001) or (_screen_fade_speed >= 0.001))
  {
    _screen_fade += _screen_fade_speed * dt;
    if ((_screen_fade <= 0.0) and (_screen_fade_speed <= -0.001))
    {
      _screen_fade = 0.0;
      _screen_fade_speed = 0.0;
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->fade_complete();
      }
    }
    if ((_screen_fade >= 1.0) and (_screen_fade_speed >= 0.001))
    {
      _screen_fade = 1.0;
      _screen_fade_speed = 0.0;
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->fade_complete();
      }
    }
  }
  
  /* Update screen flash. */
  if ((_screen_flash_speed <= -0.001) or (_screen_flash_speed >= 0.001))
  {
    _screen_flash += _screen_flash_speed * dt;
    if ((_screen_flash <= 0.0) and (_screen_flash_speed <= -0.001))
    {
      _screen_flash = 0.0;
      _screen_flash_speed = 0.0;
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->fade_complete();
      }
    }
    if ((_screen_flash >= 1.0) and (_screen_flash_speed >= 0.001))
    {
      _screen_flash = 0.0;
      _screen_flash_speed = 0.0;
      for (int i=0; i<_listeners.size(); ++i)
      {
        _listeners[i]->fade_complete();
      }
    }
  }
  
  /* Update stone camera */
  if (current_game() && current_game()->stone_played())
  {
    _stone_camera->reposition(
      vector3d(-3.0, 0.0 - (current_game()->rink_played()->length()/2.0) + current_game()->stone_played()->position().y() - 1.5, 3.5));
    _stone_camera->aim(
      vector3d(-3.0, 0.0 - (current_game()->rink_played()->length()/2.0) + current_game()->stone_played()->position().y() + 1.0, 0.0));
  }
  
  /* Update arena camera */
  //_arena_camera->aim(
  //  vector3d(20.0*std::cos(2*3.1415*t*-0.1), 20.0*std::sin(2*3.1415*t*-0.1), 0.0));
      
  if (powermeter_visible())
  {
    /* Update Power-Meter POWER */
    if (_powermeter_current_state == powermeter_state_power)
    {
      _powermeter_current_power += _powermeter_mode*_powermeter_power_speed*dt;
      if (_powermeter_current_power <= 0.0)
      {
        _powermeter_current_power = 0.0;
        _powermeter_mode *= -1.0;
      }
      if (_powermeter_current_power >= 1.0)
      {
        _powermeter_current_power = 1.0;
        _powermeter_mode *= -1.0;
      }
    }
  
    /* Update Power-Meter DIRECTION */
    if (_powermeter_current_state == powermeter_state_direction)
    {
      _powermeter_current_direction += _powermeter_mode*_powermeter_direction_speed*dt;
      if (_powermeter_current_direction <= -1.0)
      {
        _powermeter_current_direction = -1.0;
        _powermeter_mode *= -1.0;
      }
      if (_powermeter_current_direction >= 1.0)
      {
        _powermeter_current_direction = 1.0;
        _powermeter_mode *= -1.0;
      }
    }
  
    /* Update Power-Meter SPIN */
    if (_powermeter_current_state == powermeter_state_spin)
    {
      _powermeter_current_spin += _powermeter_mode*_powermeter_spin_speed*dt;
      if (_powermeter_current_spin <= -1.0)
      {
        _powermeter_current_spin = -1.0;
        _powermeter_mode *= -1.0;
      }
      if (_powermeter_current_spin >= 1.0)
      {
        _powermeter_current_spin = 1.0;
        _powermeter_mode *= -1.0;
      }
    }
  }
  
  /* Update camera animation. */
  if (_camera_animation_length > 0.0)
  {
    _camera_animation_time += dt;
    if (_camera_animation_time > _camera_animation_length)
    {
      _camera_animation_time = 0.0;
      _camera_animation_length = 0.0;
      _current_camera = _animation_target_camera;
    }
    else
    {
      _animated_camera->reposition(
        _animation_start_camera->position() +
        (((_animation_target_camera->position() - _animation_start_camera->position()) /
          _camera_animation_length) * _camera_animation_time));
      _animated_camera->aim(
        _animation_start_camera->target() + 
        (((_animation_target_camera->target() - _animation_start_camera->target()) /
          _camera_animation_length) * _camera_animation_time));
    }
  }
  
  /* Update message. */
  if (_message_time > -1.0)
  {
    _message_time -= dt;
  }
  else
  {
    _message_time = -2.0;
  }
  
  /* Update character animation. */
  if (_game_character_controller)
  {
    _game_character_controller->animate(t, dt);
  }
}

void gui::add_listener( boost::shared_ptr<gui_listener> listener )
{
  _listeners.push_back(listener);
}

void gui::remove_listener( boost::shared_ptr<gui_listener> listener )
{
  //FIXME!
}

void gui::focus_item()
{
  for (size_t i=0; i<_listeners.size(); ++i)
  {
    _listeners[i]->focus_item();
  }
}

void gui::select_item()
{
  for (size_t i=0; i<_listeners.size(); ++i)
  {
    _listeners[i]->select_item();
  }
}

}
